#pragma once

#include <vector>
#include <unordered_map>
#include <memory>
#include <functional>

#include <sys/timerfd.h>

#include "Logger.hpp"
#include "Channel.hpp"
#include "Socket.hpp"

using namespace SocketModule;
using namespace std;
using namespace LogMudule;


// 前置声明
class Reactor;

// 定时任务类型
typedef function<void()> timer_task_t;

//默认秒级时间轮的大小（默认1024）
static const uint32_t DefaultWheelCapacity = 1024;


//定时任务
class TimerTask
{
private:
    uint64_t _id;                               // 定时任务对象的ID
    uint32_t _timeout;                          // 定时任务多少秒之后执行
    timer_task_t _task_func;                    // 时间到了要执行的任务
    function<void(uint64_t)> _relrase_callback; // 用于以回调的方式删除TimerWheel保存的“指向定时任务对象的weak_ptr智能指针”
    bool _canceled;                             // 标识该定时任务是否被取消（true表示被取消）
public:
    TimerTask(uint64_t id, uint32_t timeout, const timer_task_t &task)
        : _id(id),
          _timeout(timeout),
          _canceled(false),
          _task_func(task),
          _relrase_callback(nullptr)
    {
    }

    void SetRelraseCallback(function<void(uint64_t)> relrase)
    {
        _relrase_callback = relrase;
    }
    // 获取超时时间
    uint32_t Timeout()
    {
        return _timeout;
    }
    // 取消定时任务
    void Canceled()
    {
        _canceled = true;
    }
    ~TimerTask()
    {
        // 定时任务销毁时，主动删除TimerWheel中管理自己的信息
        if (_relrase_callback)
        {
            _relrase_callback(_id);
        }
        else
        {
            LOG(LogLevel::DEADLY)<<"没有为TimerTask添加删除回调";
        }
        // 如果没有取消定时任务
        if (_canceled == false)
        {
            // 执行定时任务
            LOG(LogLevel::DEBUG) << "执行定时任务";
            _task_func();
        }
    }
};


//定时任务模块，让一个任务可以在指定时间之后被执行
class TimerWheel
{
private:
    uint32_t _capacity;                                  // 时间轮数组的容量，本质就是最大的可设置的超时时间
    vector<vector<shared_ptr<TimerTask>>> _wheel;        // 时间轮数组，根据超时时间存放定时任务对象
    int _tick;                                           // 当前的秒针，指向时间轮数组，指向数组的那个下标，对应下标位置的定时任务就需要被执行了
    unordered_map<uint64_t, weak_ptr<TimerTask>> _tasks; // 使用weak_ptr保存定时任务（TimerTask）对象，后续刷新定时任务的超时时间的时候，用来拷贝构造

    int _timerfd;
    Reactor *_belongs_reactor; // 指向所属的Reactor对象
    Channel _timer_channel;    //_timerfd所关联的Channel

private:
    // 删除哈希表_tasks中：指定ID的定时任务对象的weak_ptr
    void EraseTimerTask(uint64_t id)
    {
        // 如果指定的定时任务存在
        if (_tasks.find(id) != _tasks.end())
        {
            _tasks.erase(id);
        }
        else
        {
            LOG(LogLevel::WARNING) << "试图删除不存在的定时任务";
        }
    }
    // 创建并初始化timerfd
    static int CreateTimerFd()
    {
        // 创建timerfd
        int timerfd = timerfd_create(CLOCK_MONOTONIC, 0);
        if (timerfd < 0)
        {
            LOG(LogLevel::DEADLY) << "timerfd创建失败!!!";
            abort();
        }

        // 设置每隔1秒，超时一次
        struct itimerspec iti;
        iti.it_interval = {1, 0};
        iti.it_value = {1, 0};

        int ret = timerfd_settime(timerfd, 0, &iti, nullptr);
        if (ret < 0)
        {
            LOG(LogLevel::DEADLY) << "timerfd定时设置失败!!!";
            abort();
        }

        return timerfd;
    }
    // 模拟时间向后走time秒,执行时间轮上对应的定时任务
    void RunTimerTask(uint64_t time)
    {
        // 秒针向后移动
        for (int i = 0; i < time; i++)
        {
            // 删除当前秒针指向位置的所有定时任务对象，定时任务对象析构时会执行定时任务
            _wheel[_tick].clear();
            // 秒针向后移动
            _tick = (_tick + 1) % _capacity;
        }
    }
    //读取timerfd，获取时间过了n秒，然后秒针向后移动n秒，执行定时任务
    void ReadAndRunTimerfd()
    {
        uint64_t time = 0;
        // 读取timerfd，获取时间过了几秒了
        int ret = read(_timerfd, &time, sizeof(time));
        if (ret < 0)
        {
            LOG(LogLevel::DEADLY) << "读取timerfd异常";
            abort();
        }
        // 秒针向后移动n秒
        RunTimerTask(time);
    }
    // 在所属reactor线程内刷新定时任务
    void RefreshTimerTaskInLoop(uint64_t id, uint32_t time)
    {
        // 如果指定的定时任务存在
        if (_tasks.find(id) != _tasks.end())
        {
            // 取出保存的定时任务本源
            shared_ptr<TimerTask> task = _tasks[id].lock();
            // 得出该定时任务的新位置
            int pos = (_tick + time) % _capacity;
            // 刷新定时任务执行的时间
            _wheel[pos].push_back(task);
        }
        else
        {
            LOG(LogLevel::WARNING) << "试图刷新不存在的定时任务";
        }
    }
    // 在所属reactor线程内添加定时任务
    void AddTimerTaskInLoop(uint64_t id, uint32_t timeout, const timer_task_t &task)
    {
        // 如果指定的定时任务  不存在
        if (_tasks.find(id) == _tasks.end())
        {
            // 取出保存的定时任务本源
            shared_ptr<TimerTask> timetask = make_shared<TimerTask>(id, timeout, task);

            // 设置清理函数回调
            timetask->SetRelraseCallback(std::bind(&TimerWheel::EraseTimerTask, this, placeholders::_1));

            // 添加定时任务
            int tick = (_tick + timeout) % _capacity;
            _wheel[tick].push_back(timetask);

            // 保存定时任务本源
            weak_ptr<TimerTask> tmp(timetask);
            _tasks.insert({id, tmp});
        }
        else
        {
            LOG(LogLevel::WARNING) << "试图添加已经存在的定时任务";
        }
    }
    // 在所属reactor线程内取消定时任务
    void CanceledTimerTaskInLoop(uint64_t id)
    {
        // 如果指定的定时任务存在
        if (_tasks.find(id) != _tasks.end())
        {
            // 找到并取消定时任务
            (_tasks[id].lock())->Canceled();
        }
        else
        {
            LOG(LogLevel::WARNING) << "试图取消不存在的定时任务";
        }
    }

public:
    TimerWheel(Reactor *belongs_reactor, int cap = DefaultWheelCapacity)
        : _capacity(cap),
          _wheel(cap),
          _tick(0),
          _timerfd(CreateTimerFd()),
          _belongs_reactor(belongs_reactor),
          _timer_channel(belongs_reactor, _timerfd)
    {
        //把_timerfd设置为非阻塞
        SocketModule::FdSetNonBlock(_timerfd);

        // 设置读回调，一旦读事件就绪，就移动秒针
        _timer_channel.SetReadCallback(bind(&TimerWheel::ReadAndRunTimerfd, this));
        // 开启timerfd读事件监控
        _timer_channel.EnableRead();
    }
    // 添加定时任务
    void AddTimerTask(uint64_t id, uint32_t timeout, const timer_task_t &task);
    // 刷新定时任务，延迟超时时间
    void RefreshTimerTask(uint64_t id, uint32_t time);

    // 取消指定定时任务
    void CanceledTimerTask(uint64_t id);

    // 判断一个定时任务是否存在
    // 注意：这个接口虽然是公有的，而且是对定时任务的操作
    // 但是只是给组件内使用，并不提供提供给组件外使用，所以并不和上面3个一样需要被RunInLoop封装
    bool HasTimerTask(uint64_t id)
    {
        if (_tasks.find(id) != _tasks.end())
        {
            return true;
        }
        return false;
    }

    ~TimerWheel()
    {
        //移除_timerfd的事件监控
        _timer_channel.Remove();
        ::close(_timerfd);
    }
};